<template>
  <div class="grapesjs-editor-container">
    <!-- 顶部操作栏 -->
    <div class="top-toolbar">
      <div class="toolbar-left">
        <el-button icon="el-icon-back" @click="goBack">返回</el-button>
        <span class="page-title">可视化编辑 - {{ pageInfo.title }}</span>
        <el-select
          v-model="pageInfo.status"
          placeholder="选择状态"
          size="small"
          style="margin-left: 20px; width: 150px;"
          @change="handleStatusChange"
        >
          <el-option label="草稿" value="draft" />
          <el-option label="AI生成中" value="ai_generating" />
          <el-option label="MD已创建" value="md_created" />
          <el-option label="AI源码完成" value="source_code_generated" />
          <el-option label="图片优化完毕" value="images_optimized" />
          <el-option label="内容优化完毕" value="manual_optimized" />
          <el-option label="可发布" value="ready_to_publish" />
          <el-option label="待审核" value="review" />
          <el-option label="已发布" value="published" />
          <el-option label="已归档" value="archived" />
        </el-select>
        <el-button
          size="small"
          style="margin-left: 10px;"
          @click="showOperationNoteDialog = true"
        >
          <i v-if="operationNoteInput" class="el-icon-warning" style="color: #E6A23C;" />
          <i v-else class="el-icon-edit" />
          操作备注
        </el-button>
      </div>
      <div class="toolbar-right">
        <el-button-group>
          <el-button
            icon="el-icon-refresh-left"
            :disabled="!canUndo"
            @click="undo"
          >
            撤销 (Ctrl+Z)
          </el-button>
          <el-button
            icon="el-icon-refresh-right"
            :disabled="!canRedo"
            @click="redo"
          >
            重做 (Ctrl+Y)
          </el-button>
        </el-button-group>
        <el-button type="success" icon="el-icon-view" @click="previewInNewTab">
          新窗口预览
        </el-button>
        <el-button
          type="info"
          icon="el-icon-document-copy"
          :loading="beautifying"
          @click="beautifyCode"
        >
          美化代码
        </el-button>
        <el-button
          type="primary"
          icon="el-icon-check"
          :loading="saving"
          @click="saveContent"
        >
          保存
        </el-button>
      </div>
    </div>

    <!-- GrapeJS 编辑器 -->
    <div id="grapesjs-editor" ref="grapesEditor" class="grapes-editor" />

    <!-- AI 提示词输入弹窗 -->
    <el-dialog
      :title="aiDialogTitle"
      :visible.sync="aiDialogVisible"
      width="600px"
      append-to-body
      @close="handleAIDialogClose"
    >
      <el-form label-width="100px">
        <el-form-item label="AI 提示词">
          <el-input
            v-model="aiPrompt"
            type="textarea"
            :rows="6"
            placeholder="请输入您的需求，例如：把这个按钮改成蓝色，字体加粗"
          />
        </el-form-item>
        <el-form-item v-if="aiMessages.length > 0" label="对话历史">
          <el-tag type="info" size="small">
            已有 {{ aiMessages.length }} 条对话记录
          </el-tag>
          <el-button
            type="text"
            size="small"
            icon="el-icon-delete"
            style="margin-left: 10px;"
            @click="clearAIMessages"
          >
            清空对话
          </el-button>
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="aiDialogVisible = false">取消</el-button>
        <el-button
          type="primary"
          icon="el-icon-magic-stick"
          :loading="aiProcessing"
          style="background-color: #1EB7E6; border-color: #1EB7E6;"
          @click="handleAISubmit"
        >
          {{ aiProcessing ? 'AI 处理中...' : '执行 AI 命令' }}
        </el-button>
      </div>
    </el-dialog>

    <!-- 操作备注对话框 -->
    <el-dialog
      title="编辑操作备注"
      :visible.sync="showOperationNoteDialog"
      width="600px"
      append-to-body
    >
      <el-input
        v-model="operationNoteInput"
        type="textarea"
        :rows="6"
        placeholder="请输入操作备注，用于记录页面当前状态、待办事项等协作信息..."
        maxlength="500"
        show-word-limit
      />
      <div slot="footer">
        <el-button @click="showOperationNoteDialog = false">取消</el-button>
        <el-button
          type="primary"
          :loading="savingOperationNote"
          @click="saveOperationNote"
        >
          保存
        </el-button>
      </div>
    </el-dialog>

    <!-- 替换图片对话框 -->
    <el-dialog
      title="替换图片"
      :visible.sync="replaceImageDialogVisible"
      width="600px"
      append-to-body
    >
      <el-tabs v-model="replaceImageTab">
        <!-- URL替换 -->
        <el-tab-pane label="URL替换" name="url">
          <el-form label-width="100px">
            <el-form-item label="图片URL">
              <el-input
                v-model="replaceImageUrl"
                placeholder="请输入图片URL，例如：https://example.com/image.jpg"
                clearable
                @keyup.enter.native="handleReplaceImage"
              />
              <div style="margin-top: 8px; font-size: 12px; color: #909399;">
                提示：支持 img 标签和背景图片的替换
              </div>
            </el-form-item>
          </el-form>
        </el-tab-pane>

        <!-- AI生成 -->
        <el-tab-pane label="AI生成" name="ai">
          <el-form label-width="100px">
            <el-form-item label="图片描述">
              <el-input
                v-model="aiImagePrompt"
                type="textarea"
                :rows="4"
                placeholder="请输入图片描述，例如：一张展示产品特性的专业摄影图片"
              />
              <div style="margin-top: 8px; font-size: 12px; color: #909399;">
                提示：描述越详细，生成的图片越符合预期
              </div>
            </el-form-item>
            <el-form-item label="图片比例">
              <el-select v-model="aiImageAspectRatio" placeholder="请选择比例">
                <el-option label="1:1 正方形 (1024×1024)" value="1:1" />
                <el-option label="16:9 横向 (1344×768)" value="16:9" />
                <el-option label="9:16 纵向 (768×1344)" value="9:16" />
                <el-option label="3:2 横向 (1248×832)" value="3:2" />
                <el-option label="2:3 纵向 (832×1248)" value="2:3" />
                <el-option label="4:3 横向 (1184×864)" value="4:3" />
                <el-option label="3:4 纵向 (864×1184)" value="3:4" />
                <el-option label="21:9 超宽 (1536×672)" value="21:9" />
              </el-select>
              <div style="margin-top: 8px; font-size: 12px; color: #909399;">
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-refresh"
                  :loading="generatingPrompt"
                  @click="regeneratePrompt"
                >
                  {{ generatingPrompt ? '正在生成...' : '重新生成Prompt' }}
                </el-button>
              </div>
            </el-form-item>

            <!-- 压缩配置 -->
            <el-form-item label="压缩模式">
              <el-radio-group v-model="toolbarCompressMode" size="small">
                <el-radio label="original">原图压缩</el-radio>
                <el-radio label="2x">2倍宽度（推荐）</el-radio>
                <el-radio label="custom">自定义宽度</el-radio>
              </el-radio-group>
              <el-input-number
                v-if="toolbarCompressMode === 'custom'"
                v-model="toolbarCompressWidth"
                :min="100"
                :max="8000"
                :step="100"
                size="small"
                style="margin-left: 10px;"
              />
              <span v-if="toolbarCompressMode === 'custom'" style="margin-left: 5px; color: #909399; font-size: 12px;">px</span>
            </el-form-item>

            <el-form-item label="压缩质量">
              <el-slider
                v-model="toolbarCompressQuality"
                :min="1"
                :max="100"
                :marks="{ 1: '最小', 50: '中等', 100: '最高' }"
                show-input
                style="width: 300px;"
              />
              <div style="margin-top: 8px; font-size: 12px; color: #909399;">
                quality=100 表示最高质量（推荐）
              </div>
            </el-form-item>

            <!-- 实时日志显示 -->
            <el-form-item v-if="aiImageGenerationLog" label="生成过程">
              <div
                style="
                  max-height: 200px;
                  overflow-y: auto;
                  padding: 10px;
                  background: #f5f7fa;
                  border-radius: 4px;
                  font-size: 12px;
                  white-space: pre-wrap;
                  font-family: monospace;
                "
              >
                {{ aiImageGenerationLog }}
              </div>
            </el-form-item>
          </el-form>
        </el-tab-pane>
      </el-tabs>

      <div slot="footer">
        <el-button @click="replaceImageDialogVisible = false">取消</el-button>
        <el-button
          v-if="replaceImageTab === 'url'"
          type="primary"
          icon="el-icon-picture"
          @click="handleReplaceImage"
        >
          确定替换
        </el-button>
        <el-button
          v-if="replaceImageTab === 'ai'"
          type="primary"
          icon="el-icon-magic-stick"
          style="background-color: #1EB7E6; border-color: #1EB7E6;"
          :loading="aiGeneratingImage"
          @click="handleAIGenerateImage"
        >
          {{ aiGeneratingImage ? 'AI 生成中...' : 'AI 生成图片' }}
        </el-button>
      </div>
    </el-dialog>

    <!-- 图片检测对话框 -->
    <el-dialog
      title="页面图片列表"
      :visible.sync="randomImagesDialogVisible"
      width="1200px"
      append-to-body
      @close="randomImagesDialogVisible = false"
    >
      <div v-if="randomImages.length === 0" style="text-align: center; padding: 40px; color: #909399;">
        未检测到图片
      </div>

      <template v-else>
        <!-- 快速选择按钮 -->
        <div style="margin-bottom: 15px; padding: 10px; background: #f5f7fa; border-radius: 4px;">
          <el-button-group>
            <el-button
              size="small"
              icon="el-icon-check"
              @click="selectAllRandomImages"
            >
              选择所有随机图片 ({{ randomImages.filter(i => i.isRandomImage && i.type !== 'background').length }})
            </el-button>
            <el-button
              size="small"
              icon="el-icon-check"
              @click="selectAllImages"
            >
              全选 ({{ randomImages.length }})
            </el-button>
            <el-button
              size="small"
              icon="el-icon-close"
              @click="clearSelection"
            >
              清空选择
            </el-button>
          </el-button-group>
        </div>

        <el-table
          ref="randomImagesTable"
          :data="randomImages"
          style="width: 100%"
          max-height="500"
          :row-class-name="tableRowClassName"
          @selection-change="handleSelectionChange"
        >
          <el-table-column
            type="selection"
            width="55"
          />

          <el-table-column
            label="预览"
            width="100"
          >
            <template slot-scope="scope">
              <img
                :src="scope.row.src"
                :alt="scope.row.alt"
                style="width: 80px; height: 60px; object-fit: cover; border-radius: 4px;"
              >
            </template>
          </el-table-column>

          <el-table-column
            label="图片信息"
            min-width="250"
            show-overflow-tooltip
          >
            <template slot-scope="scope">
              <div style="font-size: 12px; line-height: 1.5;">
                <div style="margin-bottom: 3px;">
                  <el-tag
                    :type="scope.row.type === 'img' ? 'success' : 'warning'"
                    size="mini"
                    style="margin-right: 5px;"
                  >
                    {{ scope.row.type === 'img' ? 'IMG' : 'CSS' }}
                  </el-tag>
                  <el-tag
                    :type="scope.row.isRandomImage ? 'danger' : 'info'"
                    size="mini"
                    style="margin-right: 5px;"
                  >
                    {{ scope.row.isRandomImage ? '随机' : '常规' }}
                  </el-tag>
                  <span style="color: #606266; font-weight: 500;">{{ scope.row.alt }}</span>
                </div>
                <div style="color: #909399; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                  {{ scope.row.src }}
                </div>
              </div>
            </template>
          </el-table-column>

          <el-table-column
            label="生成尺寸"
            width="180"
            align="center"
          >
            <template slot-scope="scope">
              <el-select
                v-model="scope.row.aspectRatio"
                size="mini"
                placeholder="选择尺寸"
              >
                <el-option label="1:1 正方形 (1024×1024)" value="1:1" />
                <el-option label="16:9 横向 (1344×768)" value="16:9" />
                <el-option label="9:16 纵向 (768×1344)" value="9:16" />
                <el-option label="3:2 横向 (1248×832)" value="3:2" />
                <el-option label="2:3 纵向 (832×1248)" value="2:3" />
                <el-option label="4:3 横向 (1184×864)" value="4:3" />
                <el-option label="3:4 纵向 (864×1184)" value="3:4" />
                <el-option label="21:9 超宽 (1536×672)" value="21:9" />
              </el-select>
            </template>
          </el-table-column>

          <el-table-column
            label="压缩配置"
            width="300"
            align="center"
          >
            <template slot-scope="scope">
              <div style="font-size: 12px;">
                <div style="margin-bottom: 5px;">
                  <el-select
                    v-model="scope.row.compressMode"
                    size="mini"
                    placeholder="压缩模式"
                    style="width: 140px;"
                  >
                    <el-option label="原图压缩" value="original" />
                    <el-option label="2倍宽度" value="2x" />
                    <el-option label="自定义" value="custom" />
                  </el-select>
                  <el-input-number
                    v-if="scope.row.compressMode === 'custom'"
                    v-model="scope.row.compressWidth"
                    size="mini"
                    :min="100"
                    :max="8000"
                    :step="100"
                    controls-position="right"
                    style="width: 120px; margin-left: 5px;"
                  />
                  <span v-else-if="scope.row.compressMode === '2x'" style="color: #909399; margin-left: 5px;">
                    {{ scope.row.detectedWidth ? `${scope.row.detectedWidth * 2}px` : '自动' }}
                  </span>
                </div>
                <div>
                  <span style="color: #606266; margin-right: 5px;">质量:</span>
                  <el-slider
                    v-model="scope.row.compressQuality"
                    :min="1"
                    :max="100"
                    :show-tooltip="false"
                    style="width: 120px; display: inline-block; margin-right: 5px;"
                  />
                  <span style="color: #409EFF; font-weight: bold;">{{ scope.row.compressQuality }}%</span>
                </div>
              </div>
            </template>
          </el-table-column>

          <el-table-column
            label="操作"
            width="260"
            align="center"
            fixed="right"
          >
            <template slot-scope="scope">
              <el-button
                size="mini"
                type="primary"
                @click="selectRandomImage(scope.row)"
              >
                定位
              </el-button>
              <el-button
                size="mini"
                type="success"
                style="background-color: #1EB7E6; border-color: #1EB7E6;"
                icon="el-icon-magic-stick"
                :loading="scope.row.generating"
                @click="autoGenerateAndReplace(scope.row)"
              >
                AI生成
              </el-button>
              <el-button
                size="mini"
                type="warning"
                icon="el-icon-upload"
                @click="handleManualReplace(scope.row)"
              >
                手动替换
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </template>

      <div slot="footer" style="text-align: center;">
        <el-alert
          type="info"
          :closable="false"
          style="margin-bottom: 15px;"
        >
          检测到 {{ randomImages.length }} 张图片 (随机图片: {{ randomImages.filter(i => i.isRandomImage).length }}, 常规图片: {{ randomImages.filter(i => !i.isRandomImage).length }})，已选中 {{ selectedRandomImages.length }} 张
        </el-alert>
        <el-button @click="randomImagesDialogVisible = false">关闭</el-button>
        <el-button
          v-if="selectedRandomImages.length > 0"
          type="success"
          style="background-color: #1EB7E6; border-color: #1EB7E6;"
          icon="el-icon-magic-stick"
          :loading="batchGenerating"
          @click="batchAutoGenerateAndReplace"
        >
          批量AI生成 ({{ selectedRandomImages.length }})
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import request from '@/utils/request'
import { CheckFiles } from '@/api/static_web/page'
import { html as beautifyHtml } from 'js-beautify'

export default {
  name: 'GrapesJSEditor',
  data() {
    return {
      pageId: null,
      pageInfo: {
        title: '',
        slug: ''
      },
      currentHtmlContent: '', // 完整的原始HTML（从后台获取，用于AI图片替换）
      // 保存原始HTML的各个部分，用于重新组合
      originalHtmlParts: {
        doctype: '<!DOCTYPE html>',
        htmlAttr: '',
        headContent: '',
        bodyAttr: '',
        bodyScripts: '' // body底部的script标签
      },
      grapesJSEditor: null,
      saving: false,
      beautifying: false,
      aiDialogVisible: false,
      aiDialogTitle: '',
      aiPrompt: '',
      aiProcessing: false,
      aiActionType: '', // 'edit' 或 'add'
      selectedComponent: null,
      aiMessages: [], // AI 对话历史
      canUndo: false, // 是否可以撤销
      canRedo: false, // 是否可以重做
      replaceImageDialogVisible: false, // 替换图片对话框
      replaceImageUrl: '', // 替换的图片URL
      replaceImageComponent: null, // 待替换图片的组件
      replaceImageTab: 'url', // 当前选项卡: url | ai
      aiImagePrompt: '', // AI生成图片的prompt
      aiImageAspectRatio: '1:1', // AI生成图片的比例
      aiGeneratingImage: false, // AI生成图片中
      generatingPrompt: false, // 正在生成prompt
      imageContext: '', // 图片上下文（HTML周围内容）
      aiImageGenerationLog: '', // AI图片生成实时日志
      randomImagesDialogVisible: false, // 随机图片列表对话框
      randomImages: [], // 随机图片列表
      selectedRandomImages: [], // 选中的随机图片
      batchGenerating: false, // 批量生成中
      // 工具栏图片替换的压缩配置
      toolbarCompressMode: '2x', // 压缩模式: original | 2x | custom
      toolbarCompressWidth: 800, // 自定义宽度
      toolbarCompressQuality: 100, // 压缩质量
      // 操作备注相关
      showOperationNoteDialog: false, // 操作备注对话框
      operationNoteInput: '', // 操作备注输入
      savingOperationNote: false // 保存操作备注中
    }
  },
  created() {
    this.pageId = this.$route.params.id || this.$route.query.id
    if (!this.pageId) {
      this.$message.error('缺少页面ID')
      this.goBack()
      return
    }
    this.loadPageData()
  },
  beforeDestroy() {
    // 销毁 GrapeJS 编辑器
    if (this.grapesJSEditor) {
      this.grapesJSEditor.destroy()
      this.grapesJSEditor = null
    }
  },
  methods: {
    // 加载页面数据
    loadPageData() {
      const loading = this.$loading({
        lock: true,
        text: '正在加载页面...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })

      // 获取页面信息
      request({
        url: `/static_web/api/page/${this.pageId}/`,
        method: 'get'
      }).then(response => {
        this.pageInfo = response.data
        // 初始化操作备注
        this.operationNoteInput = response.data.operation_note || ''

        // 检查HTML文件是否存在
        return CheckFiles(this.pageId)
      }).then(response => {
        const fileStatus = response.data

        if (!fileStatus.html_exists) {
          loading.close()
          this.$message.warning('HTML 文件不存在，请先生成页面')
          this.goBack()
          return
        }

        // 提取图片（获取HTML内容）
        return request({
          url: `/static_web/api/page/${this.pageId}/extract_images/`,
          method: 'get'
        })
      }).then(response => {
        this.currentHtmlContent = response.data.html_content

        // 初始化 GrapeJS
        this.initGrapesJS()
        loading.close()
      }).catch(() => {
        loading.close()
        this.$message.error('加载失败')
        this.goBack()
      })
    },
    // 返回
    goBack() {
      this.$router.back()
    },
    // 状态改变处理
    async handleStatusChange(newStatus) {
      try {
        const response = await request({
          url: `/static_web/api/page/${this.pageId}/update_status/`,
          method: 'post',
          data: {
            status: newStatus
          }
        })

        if (response.success) {
          this.$message.success('状态更新成功')
        } else {
          this.$message.error(response.msg || '状态更新失败')
          // 回滚状态
          this.loadPageData()
        }
      } catch (error) {
        console.error('状态更新失败:', error)
        this.$message.error('状态更新失败')
        // 回滚状态
        this.loadPageData()
      }
    },
    // 保存操作备注
    async saveOperationNote() {
      this.savingOperationNote = true
      try {
        const response = await request({
          url: `/static_web/api/page/${this.pageId}/update_operation_note/`,
          method: 'post',
          data: {
            operation_note: this.operationNoteInput
          }
        })

        if (response.success) {
          this.$message.success('操作备注保存成功')
          this.showOperationNoteDialog = false
          this.pageInfo.operation_note = this.operationNoteInput
        } else {
          this.$message.error(response.msg || '保存失败')
        }
      } catch (error) {
        console.error('保存操作备注失败:', error)
        this.$message.error('保存失败')
      } finally {
        this.savingOperationNote = false
      }
    },
    // 初始化 GrapeJS
    initGrapesJS() {
      this.$nextTick(() => {
        if (!window.grapesjs) {
          this.loadGrapesJSLibrary()
        } else {
          this.createGrapesJSEditor()
        }
      })
    },
    // 加载 GrapeJS 库
    loadGrapesJSLibrary() {
      const loading = this.$loading({
        lock: true,
        text: '正在加载可视化编辑器...',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })

      // 加载 Font Awesome（GrapeJS 工具栏图标）
      if (!document.querySelector('link[href*="font-awesome"]')) {
        const faLink = document.createElement('link')
        faLink.rel = 'stylesheet'
        faLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css'
        document.head.appendChild(faLink)
      }

      // 加载 GrapeJS CSS
      const cssLink = document.createElement('link')
      cssLink.rel = 'stylesheet'
      cssLink.href = 'https://unpkg.com/grapesjs@0.21.7/dist/css/grapes.min.css'
      document.head.appendChild(cssLink)

      // 加载 GrapeJS JS
      const script = document.createElement('script')
      script.src = 'https://unpkg.com/grapesjs@0.21.7/dist/grapes.min.js'
      script.onload = () => {
        loading.close()
        this.createGrapesJSEditor()
      }
      script.onerror = () => {
        loading.close()
        this.$message.error('加载可视化编辑器失败')
        this.goBack()
      }
      document.head.appendChild(script)
    },
    // 提取HTML各个部分（在初始化编辑器前调用）
    extractHtmlParts() {
      const html = this.currentHtmlContent

      // 提取 DOCTYPE
      const doctypeMatch = html.match(/<!DOCTYPE[^>]*>/i)
      if (doctypeMatch) {
        this.originalHtmlParts.doctype = doctypeMatch[0]
      }

      // 提取 <html> 标签属性
      const htmlAttrMatch = html.match(/<html([^>]*)>/i)
      if (htmlAttrMatch) {
        this.originalHtmlParts.htmlAttr = htmlAttrMatch[1]
      }

      // 提取 <head> 内容
      const headMatch = html.match(/<head[^>]*>([\s\S]*?)<\/head>/i)
      if (headMatch) {
        this.originalHtmlParts.headContent = headMatch[1]
      }

      // 提取 <body> 标签属性
      const bodyAttrMatch = html.match(/<body([^>]*)>/i)
      if (bodyAttrMatch) {
        this.originalHtmlParts.bodyAttr = bodyAttrMatch[1]
      }

      // 提取 body 底部的 <script> 标签（通常在 body 结束前）
      // 策略：从body尾部向前查找，提取末尾连续的script标签区域
      const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i)
      if (bodyMatch) {
        const bodyContent = bodyMatch[1]

        // 从尾部开始提取脚本：匹配body末尾的所有连续script标签
        // 这样可以保留底部的公共脚本，而不会错误提取中间的内联脚本
        const tailScriptsMatch = bodyContent.match(/((?:<script[\s\S]*?<\/script>\s*)+)\s*$/i)
        if (tailScriptsMatch) {
          this.originalHtmlParts.bodyScripts = tailScriptsMatch[1].trim()
        } else {
          // 如果没有尾部script标签，明确设置为空字符串（避免保留旧值）
          this.originalHtmlParts.bodyScripts = ''
        }
      } else {
        // 如果没有找到body标签，清空bodyScripts
        this.originalHtmlParts.bodyScripts = ''
      }

      console.log('📦 已提取原始HTML部分:', this.originalHtmlParts)
      console.log('📦 headContent 长度:', this.originalHtmlParts.headContent.length)
      console.log('📦 headContent 包含 script:', this.originalHtmlParts.headContent.includes('<script'))
      console.log('📦 bodyScripts 长度:', this.originalHtmlParts.bodyScripts.length)
      if (this.originalHtmlParts.bodyScripts) {
        console.log('📦 bodyScripts 预览:', this.originalHtmlParts.bodyScripts.substring(0, 200))
      }

      // 统计各部分的 script 数量
      const headScriptCount = (this.originalHtmlParts.headContent.match(/<script/gi) || []).length
      const bodyScriptCount = (this.originalHtmlParts.bodyScripts.match(/<script/gi) || []).length
      console.log(`📊 Script 统计 - Head: ${headScriptCount}, Body: ${bodyScriptCount}, 总计: ${headScriptCount + bodyScriptCount}`)
    },

    // 提取body内容（不包含body标签本身，但移除script）
    extractBodyContent(html) {
      // 匹配 <body> 标签及其内容
      const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i)
      if (!bodyMatch) {
        console.warn('未找到body标签，使用完整HTML')
        return html
      }

      let bodyContent = bodyMatch[1]

      // 移除 script 标签（GrapeJS 不支持 script，我们会在保存时重新添加）
      bodyContent = bodyContent.replace(/<script[\s\S]*?<\/script>/gi, '')

      console.log('📄 提取的body内容长度:', bodyContent.length)
      return bodyContent
    },

    // 提取 head 中的外部资源（CSS 和 JS）
    extractExternalResources() {
      const headContent = this.originalHtmlParts.headContent || ''
      const styles = []
      const scripts = []

      // 提取外部 CSS（<link rel="stylesheet">）
      const linkRegex = /<link[^>]+rel=["']stylesheet["'][^>]*>/gi
      const linkMatches = headContent.match(linkRegex)
      if (linkMatches) {
        linkMatches.forEach(link => {
          const hrefMatch = link.match(/href=["']([^"']+)["']/)
          if (hrefMatch && hrefMatch[1]) {
            styles.push(hrefMatch[1])
            console.log('提取外部CSS:', hrefMatch[1])
          }
        })
      }

      // 提取外部 JS（<script src="">）
      const scriptRegex = /<script[^>]+src=["']([^"']+)["'][^>]*><\/script>/gi
      let scriptMatch
      while ((scriptMatch = scriptRegex.exec(headContent)) !== null) {
        if (scriptMatch[1]) {
          scripts.push(scriptMatch[1])
          console.log('提取外部JS:', scriptMatch[1])
        }
      }

      console.log(`✅ 提取到 ${styles.length} 个外部CSS，${scripts.length} 个外部JS`)

      return { styles, scripts }
    },

    // 格式化HTML（使用 js-beautify 进行专业的代码美化）
    formatHtml(html) {
      try {
        // 使用 js-beautify 进行 HTML 美化
        return beautifyHtml(html, {
          indent_size: 2, // 缩进2个空格
          indent_char: ' ', // 使用空格缩进
          max_preserve_newlines: 1, // 最多保留1个空行
          preserve_newlines: true, // 保留换行
          indent_inner_html: true, // 缩进 <head> 和 <body> 内部内容
          wrap_line_length: 0, // 不自动换行（保持标签完整性）
          wrap_attributes: 'auto', // 自动换行属性
          unformatted: ['code', 'pre', 'script', 'style'], // 不格式化这些标签的内容
          content_unformatted: ['pre', 'textarea'], // 这些标签的内容保持原样
          end_with_newline: false, // 文件末尾不添加换行
          extra_liners: [] // 不在特定标签前添加额外空行
        })
      } catch (e) {
        console.error('HTML格式化失败:', e)
        return html
      }
    },

    // 创建 GrapeJS 编辑器
    createGrapesJSEditor() {
      try {
        if (this.grapesJSEditor) {
          this.grapesJSEditor.destroy()
        }

        // 提取并保存原始HTML的各个部分（用于保存时重新组合）
        this.extractHtmlParts()

        // 提取 head 中的外部资源
        const externalResources = this.extractExternalResources()

        const editor = window.grapesjs.init({
          container: '#grapesjs-editor',
          height: '100%',
          width: '100%',
          fromElement: false,
          storageManager: false,
          // 启用撤销/重做管理器
          undoManager: {
            trackSelection: false // 不跟踪选择变化，只跟踪实际修改
          },
          // 快捷键配置
          keymaps: {
            defaults: {
              'core:undo': {
                keys: '⌘+z, ctrl+z',
                handler: 'core:undo'
              },
              'core:redo': {
                keys: '⌘+shift+z, ctrl+shift+z, ctrl+y',
                handler: 'core:redo'
              }
            }
          },
          // 配置 canvas，加载外部 CSS 和 JS
          canvas: {
            styles: externalResources.styles,
            scripts: externalResources.scripts
          }
        })

        // 只加载 body 内容到 GrapeJS（不包含 body 标签本身）
        const bodyContentOnly = this.extractBodyContent(this.currentHtmlContent)
        editor.setComponents(bodyContentOnly)

        // 加载内联 CSS
        const styleMatch = this.currentHtmlContent.match(/<style[^>]*>([\s\S]*?)<\/style>/i)
        if (styleMatch) {
          editor.setStyle(styleMatch[1])
        }

        this.grapesJSEditor = editor

        // 添加 AI 命令
        this.addAICommands(editor)

        // 添加组件工具栏 AI 按钮
        this.addAIComponentToolbar(editor)

        // 添加随机图片检测按钮
        this.addRandomImageDetector(editor)

        // 监听撤销/重做状态变化
        this.setupUndoRedoListeners(editor)

        this.$message.success('可视化编辑器加载成功')
      } catch (e) {
        console.error('创建GrapeJS编辑器失败', e)
        this.$message.error('创建编辑器失败')
      }
    },
    // 设置撤销/重做监听器
    setupUndoRedoListeners(editor) {
      const um = editor.UndoManager

      // 更新撤销/重做按钮状态
      const updateUndoRedoState = () => {
        this.canUndo = um.hasUndo()
        this.canRedo = um.hasRedo()
      }

      // 初始化状态
      updateUndoRedoState()

      // 监听变化
      editor.on('undo', updateUndoRedoState)
      editor.on('redo', updateUndoRedoState)
      editor.on('change:changesCount', updateUndoRedoState)

      console.log('撤销/重做监听器已设置')
    },
    // 撤销
    undo() {
      if (this.grapesJSEditor && this.canUndo) {
        this.grapesJSEditor.UndoManager.undo()
        const stack = this.grapesJSEditor.UndoManager.getStack()
        console.log('执行撤销，剩余', stack.length, '步可撤销')
      }
    },
    // 重做
    redo() {
      if (this.grapesJSEditor && this.canRedo) {
        this.grapesJSEditor.UndoManager.redo()
        console.log('执行重做')
      }
    },
    // 清空撤销历史（可选）
    clearUndoHistory() {
      if (this.grapesJSEditor) {
        this.grapesJSEditor.UndoManager.clear()
        this.canUndo = false
        this.canRedo = false
        console.log('撤销历史已清空')
      }
    },
    // 添加 AI 命令
    addAICommands(editor) {
      // 命令：使用 AI 编辑组件
      editor.Commands.add('ai-edit-component', {
        run: (editor) => {
          const selected = editor.getSelected()
          if (!selected) {
            this.$message.warning('请先选择一个组件')
            return
          }

          // 检查是否切换了组件，如果切换了就清空对话历史
          const selectedId = selected.getId()
          if (this.selectedComponent && this.selectedComponent.getId() !== selectedId) {
            console.log('切换了组件，清空对话历史')
            this.aiMessages = []
          }

          this.selectedComponent = selected
          this.aiActionType = 'edit'
          this.aiDialogTitle = 'AI 编辑组件'
          this.aiPrompt = ''
          this.aiDialogVisible = true
        }
      })

      // 命令：使用 AI 添加组件
      editor.Commands.add('ai-add-component', {
        run: (editor) => {
          const selected = editor.getSelected()
          this.selectedComponent = selected // 可能为 null，表示添加到 body
          this.aiActionType = 'add'
          this.aiDialogTitle = 'AI 添加组件'
          this.aiPrompt = ''
          this.aiDialogVisible = true
        }
      })

      // 命令：替换图片
      editor.Commands.add('replace-image', {
        run: (editor) => {
          const selected = editor.getSelected()
          if (!selected) {
            this.$message.warning('请先选择一个组件')
            return
          }

          // 检查是否是图片或有背景图的组件
          const tagName = selected.get('tagName')
          const styles = selected.getStyle()
          const hasBackgroundImage = styles && (styles['background-image'] || styles.backgroundImage)

          if (tagName !== 'img' && !hasBackgroundImage) {
            this.$message.warning('选中的组件不是图片或没有背景图')
            return
          }

          // 获取当前图片URL
          let currentUrl = ''
          if (tagName === 'img') {
            currentUrl = selected.getAttributes().src || ''
          } else if (hasBackgroundImage) {
            const bgImage = styles['background-image'] || styles.backgroundImage
            // 从 url("...") 中提取URL
            const match = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/)
            if (match) {
              currentUrl = match[1]
            }
          }

          // 提取图片上下文（周围的HTML内容）
          const imageContext = this.extractImageContext(selected)

          this.replaceImageComponent = selected
          this.replaceImageUrl = currentUrl
          this.imageContext = imageContext
          this.aiImagePrompt = '正在智能生成prompt...'
          this.replaceImageTab = 'url' // 默认打开URL替换标签
          this.replaceImageDialogVisible = true

          // 异步调用后端AI生成prompt
          this.generateSmartImagePrompt(currentUrl, imageContext)
        }
      })
    },
    // 添加组件工具栏 AI 按钮
    addAIComponentToolbar(editor) {
      console.log('开始添加 AI 工具栏...')

      const domc = editor.DomComponents
      const defaultType = domc.getType('default')
      // const defaultModel = defaultType.model
      // const defaultView = defaultType.view

      console.log('默认组件类型:', defaultType)

      // 方法：在组件加载后修改所有组件的工具栏
      const addToolbarToAll = () => {
        const wrapper = editor.getWrapper()
        if (!wrapper) return

        const processComponent = (component) => {
          const toolbar = component.get('toolbar') || []
          const currentToolbar = [...toolbar]

          console.log('处理组件:', component.get('tagName'), '当前工具栏:', currentToolbar)

          // 检查是否已有 AI 按钮
          const hasAIEdit = currentToolbar.some(t => t.command === 'ai-edit-component')
          const hasAIAdd = currentToolbar.some(t => t.command === 'ai-add-component')
          const hasReplaceImage = currentToolbar.some(t => t.command === 'replace-image')

          if (!hasAIEdit) {
            currentToolbar.push({
              attributes: { class: 'fa fa-magic' },
              command: 'ai-edit-component'
            })
            console.log('添加了 AI 编辑按钮')
          }

          if (!hasAIAdd) {
            currentToolbar.push({
              attributes: { class: 'fa fa-plus-circle' },
              command: 'ai-add-component'
            })
            console.log('添加了 AI 添加按钮')
          }

          // 检查是否是图片组件或有背景图
          const tagName = component.get('tagName')
          const styles = component.getStyle()
          const hasBackgroundImage = styles && (styles['background-image'] || styles.backgroundImage)

          if ((tagName === 'img' || hasBackgroundImage) && !hasReplaceImage) {
            currentToolbar.push({
              attributes: { class: 'fa fa-image', title: '替换图片' },
              command: 'replace-image'
            })
            console.log('添加了替换图片按钮')
          }

          component.set('toolbar', currentToolbar)
          console.log('更新后的工具栏:', component.get('toolbar'))

          // 递归处理子组件
          component.components().each(child => processComponent(child))
        }

        wrapper.components().each(comp => processComponent(comp))
      }

      // 立即执行一次
      setTimeout(() => {
        console.log('开始为现有组件添加 AI 工具栏...')
        addToolbarToAll()
      }, 100)

      // 监听新添加的组件
      editor.on('component:add', (component) => {
        console.log('新组件被添加:', component)
        setTimeout(() => {
          const toolbar = component.get('toolbar') || []
          const currentToolbar = [...toolbar]

          const hasAIEdit = currentToolbar.some(t => t.command === 'ai-edit-component')
          const hasAIAdd = currentToolbar.some(t => t.command === 'ai-add-component')
          const hasReplaceImage = currentToolbar.some(t => t.command === 'replace-image')

          if (!hasAIEdit) {
            currentToolbar.push({
              attributes: { class: 'fa fa-magic' },
              command: 'ai-edit-component'
            })
          }

          if (!hasAIAdd) {
            currentToolbar.push({
              attributes: { class: 'fa fa-plus-circle' },
              command: 'ai-add-component'
            })
          }

          // 检查是否是图片组件或有背景图
          const tagName = component.get('tagName')
          const styles = component.getStyle()
          const hasBackgroundImage = styles && (styles['background-image'] || styles.backgroundImage)

          if ((tagName === 'img' || hasBackgroundImage) && !hasReplaceImage) {
            currentToolbar.push({
              attributes: { class: 'fa fa-image', title: '替换图片' },
              command: 'replace-image'
            })
          }

          component.set('toolbar', currentToolbar)
        }, 10)
      })

      console.log('AI 工具栏配置完成')
    },
    // 保存内容
    saveContent() {
      if (!this.grapesJSEditor) {
        this.$message.warning('编辑器未初始化')
        return
      }

      // 用户编辑保存：只替换body内容和更新style
      let newHtml
      try {
        // 获取 GrapeJS 编辑的内容（只有 body 内容，无 body 标签）
        let bodyHtml = this.grapesJSEditor.getHtml()
        const css = this.grapesJSEditor.getCss()

        console.log('=== 用户编辑保存 ===')
        console.log('Body HTML 长度:', bodyHtml.length)
        console.log('CSS 长度:', css.length)

        // 格式化 body HTML
        bodyHtml = this.formatHtml(bodyHtml)

        // 步骤1: 从完整HTML开始
        newHtml = this.currentHtmlContent

        // 步骤2: 更新或插入 <style data-gjs-type="wrapper">（GrapeJS生成的样式）
        const grapesJsStyleTag = `<style data-gjs-type="wrapper">\n${css}\n  </style>`

        if (/<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i.test(newHtml)) {
          // 替换现有的GrapeJS style标签
          newHtml = newHtml.replace(
            /<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i,
            grapesJsStyleTag
          )
          console.log('✓ 已更新GrapeJS样式标签')
        } else {
          // 插入新的GrapeJS style标签（插入到</head>之前）
          newHtml = newHtml.replace(
            /(<\/head>)/i,
            `  ${grapesJsStyleTag}\n$1`
          )
          console.log('✓ 已插入GrapeJS样式标签')
        }

        // 步骤3: 替换body内容（保留body标签属性和底部scripts）
        const bodyReplacement = `<body${this.originalHtmlParts.bodyAttr}>
${bodyHtml}
${this.originalHtmlParts.bodyScripts}
</body>`

        newHtml = newHtml.replace(
          /<body[^>]*>[\s\S]*?<\/body>/i,
          bodyReplacement
        )

        this.currentHtmlContent = newHtml
        console.log('✅ HTML更新完成，长度:', newHtml.length)
      } catch (e) {
        console.error('❌ 保存失败', e)
        this.$message.error(`保存失败: ${e.message}`)
        return
      }

      // 保存到服务器
      this.saving = true

      request({
        url: `/static_web/api/page/${this.pageId}/save_html/`,
        method: 'post',
        data: {
          html_content: this.currentHtmlContent
        }
      }).then(() => {
        this.saving = false
        this.$message.success('保存成功')
      }).catch(error => {
        this.saving = false
        this.$message.error(`保存失败: ${error.message || '未知错误'}`)
      })
    },
    // 新窗口预览
    previewInNewTab() {
      CheckFiles(this.pageId).then(response => {
        const fileStatus = response.data
        if (fileStatus.html_exists) {
          const previewUrl = `${process.env.VUE_APP_BASE_API}${fileStatus.html_url}`
          window.open(previewUrl, '_blank')
        } else {
          this.$message.warning('HTML 文件不存在')
        }
      })
    },
    // 美化代码
    beautifyCode() {
      if (!this.grapesJSEditor) {
        this.$message.warning('编辑器未初始化')
        return
      }

      this.beautifying = true

      try {
        // 获取当前的 HTML 和 CSS
        let bodyHtml = this.grapesJSEditor.getHtml()
        const css = this.grapesJSEditor.getCss()

        console.log('美化前 HTML 长度:', bodyHtml.length)

        // 使用 formatHtml 美化 body 内容
        bodyHtml = this.formatHtml(bodyHtml)

        console.log('美化后 HTML 长度:', bodyHtml.length)

        // 重新设置到编辑器（这会触发重新渲染）
        this.grapesJSEditor.setComponents(bodyHtml)

        // 步骤1: 从完整HTML开始
        let newHtml = this.currentHtmlContent

        // 步骤2: 更新或插入 <style data-gjs-type="wrapper">（GrapeJS生成的样式）
        const grapesJsStyleTag = `<style data-gjs-type="wrapper">\n${css}\n  </style>`

        if (/<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i.test(newHtml)) {
          // 替换现有的GrapeJS style标签
          newHtml = newHtml.replace(
            /<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i,
            grapesJsStyleTag
          )
        } else {
          // 插入新的GrapeJS style标签（插入到</head>之前）
          newHtml = newHtml.replace(
            /(<\/head>)/i,
            `  ${grapesJsStyleTag}\n$1`
          )
        }

        // 步骤3: 替换body内容（保留body标签属性和底部scripts）
        const bodyReplacement = `<body${this.originalHtmlParts.bodyAttr}>
${bodyHtml}
${this.originalHtmlParts.bodyScripts}
</body>`

        newHtml = newHtml.replace(
          /<body[^>]*>[\s\S]*?<\/body>/i,
          bodyReplacement
        )

        this.currentHtmlContent = newHtml

        this.$message.success('代码美化完成！建议保存以更新文件')
      } catch (error) {
        console.error('美化代码失败:', error)
        this.$message.error('美化代码失败: ' + error.message)
      } finally {
        this.beautifying = false
      }
    },
    // 关闭 AI 对话框
    handleAIDialogClose() {
      this.aiPrompt = ''
      this.aiProcessing = false
      // 注意：不清空 aiMessages，保留对话历史用于连续对话
    },
    // 清空 AI 对话历史
    clearAIMessages() {
      this.$confirm('确定要清空对话历史吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.aiMessages = []
        this.$message.success('对话历史已清空')
      }).catch(() => {
        // 取消操作
      })
    },
    // 替换图片
    handleReplaceImage() {
      if (!this.replaceImageUrl.trim()) {
        this.$message.warning('请输入图片URL')
        return
      }

      if (!this.replaceImageComponent) {
        this.$message.error('未选择组件')
        return
      }

      const component = this.replaceImageComponent
      const tagName = component.get('tagName')
      const newUrl = this.replaceImageUrl.trim()

      try {
        // 获取旧URL
        let oldUrl = ''
        if (tagName === 'img') {
          oldUrl = component.getAttributes().src || ''
        } else {
          const styles = component.getStyle()
          const oldBgImage = styles['background-image'] || styles.backgroundImage
          if (oldBgImage) {
            const match = oldBgImage.match(/url\(['"]?([^'"]+)['"]?\)/)
            if (match) {
              oldUrl = match[1]
            }
          }
        }

        if (!oldUrl) {
          this.$message.warning('未找到旧的图片URL')
          return
        }

        console.log('[工具栏替换] 旧URL:', oldUrl)
        console.log('[工具栏替换] 新URL:', newUrl)

        // 使用统一的替换方法（自动保存）
        this.replaceImage(oldUrl, newUrl).then(replaced => {
          if (!replaced) {
            this.$message.error('替换失败，未找到旧URL')
            return
          }

          this.$message.success('✓ 图片已替换并保存')
        }).catch(error => {
          this.$message.error('替换失败: ' + error.message)
        })

        // 关闭对话框
        this.replaceImageDialogVisible = false
        this.replaceImageUrl = ''
        this.replaceImageComponent = null
      } catch (error) {
        console.error('替换图片失败:', error)
        this.$message.error('替换图片失败: ' + error.message)
      }
    },
    // 提交 AI 命令
    handleAISubmit() {
      if (!this.aiPrompt.trim()) {
        this.$message.warning('请输入 AI 提示词')
        return
      }

      this.aiProcessing = true

      if (this.aiActionType === 'edit') {
        this.executeAIEdit()
      } else if (this.aiActionType === 'add') {
        this.executeAIAdd()
      }
    },
    // 执行 AI 编辑
    executeAIEdit() {
      if (!this.selectedComponent) {
        this.$message.warning('未选择组件')
        this.aiProcessing = false
        return
      }

      // 简化：只发送组件ID和HTML内容
      const componentId = this.selectedComponent.getId()
      const componentHTML = this.selectedComponent.toHTML()

      // 构建当前消息
      const currentMessage = {
        role: 'user',
        content: this.aiPrompt
      }

      // 添加到对话历史
      const messages = [...this.aiMessages, currentMessage]

      console.log('发送给 AI 的数据:', {
        id: componentId,
        content: componentHTML,
        messages: messages
      })

      // 调用后端 AI API
      request({
        url: '/static_web/api/ai/edit_component/',
        method: 'post',
        data: {
          component_id: componentId,
          component_html: componentHTML,
          prompt: this.aiPrompt,
          messages: messages, // 传递对话历史
          page_id: this.pageId
        }
      }).then(response => {
        console.log('AI 返回的原始响应:', response)

        const newHTML = response.data.html

        if (!newHTML) {
          this.$message.error('AI 返回数据为空')
          this.aiProcessing = false
          return
        }

        console.log('AI 返回的新 HTML:', newHTML)

        // 直接替换组件
        this.selectedComponent.replaceWith(newHTML)

        // 更新对话历史
        this.aiMessages = messages
        this.aiMessages.push({
          role: 'assistant',
          content: `已将组件更新为：\n${newHTML.substring(0, 200)}...`
        })

        console.log('对话历史已更新，共', this.aiMessages.length, '条消息')

        this.$message.success('AI 编辑完成')
        this.aiDialogVisible = false
        this.aiPrompt = '' // 清空输入框
        this.aiProcessing = false
      }).catch(error => {
        console.error('AI 编辑出错:', error)
        this.$message.error(`AI 编辑失败: ${error.message || '未知错误'}`)
        this.aiProcessing = false
      })
    },
    // 执行 AI 添加
    executeAIAdd() {
      // 调用后端 AI API
      request({
        url: '/static_web/api/ai/add_component/',
        method: 'post',
        data: {
          prompt: this.aiPrompt,
          page_id: this.pageId
        }
      }).then(response => {
        const newHtml = response.data.html

        // 添加新组件
        if (this.selectedComponent) {
          // 添加到选中组件之后
          this.selectedComponent.append(newHtml)
        } else {
          // 添加到 body
          const wrapper = this.grapesJSEditor.getWrapper()
          wrapper.append(newHtml)
        }

        this.$message.success('AI 添加组件完成')
        this.aiDialogVisible = false
        this.aiProcessing = false
      }).catch(error => {
        this.$message.error(`AI 添加失败: ${error.message || '未知错误'}`)
        this.aiProcessing = false
      })
    },
    // 提取图片上下文（周围的HTML内容）
    extractImageContext(component) {
      try {
        // 获取组件的HTML
        const componentHtml = component.toHTML()

        // 获取父组件
        const parent = component.parent()
        if (!parent) return componentHtml

        // 获取父组件的文本内容和周围元素
        const parentHtml = parent.toHTML()

        // 提取图片周围的文本（前后200字符）
        const imgIndex = parentHtml.indexOf(componentHtml)
        if (imgIndex === -1) return componentHtml

        const start = Math.max(0, imgIndex - 200)
        const end = Math.min(parentHtml.length, imgIndex + componentHtml.length + 200)
        const context = parentHtml.substring(start, end)

        return context
      } catch (error) {
        console.error('提取图片上下文失败:', error)
        return ''
      }
    },

    // 智能生成图片prompt
    async generateSmartImagePrompt(imageSrc, imageContext) {
      this.generatingPrompt = true

      try {
        const response = await request({
          url: '/static_web/api/ai/generate_image_prompt/',
          method: 'post',
          data: {
            page_id: this.pageId,
            image_context: imageContext,
            image_src: imageSrc,
            aspect_ratio: this.aiImageAspectRatio
          }
        })

        if (response.success && response.data && response.data.prompt) {
          this.aiImagePrompt = response.data.prompt
          this.$message.success('智能生成Prompt成功')
        } else {
          throw new Error('生成失败')
        }
      } catch (error) {
        console.error('生成prompt失败:', error)
        this.aiImagePrompt = 'A professional, high-quality image'
        this.$message.warning('智能生成Prompt失败，请手动输入')
      } finally {
        this.generatingPrompt = false
      }
    },

    // 重新生成prompt
    regeneratePrompt() {
      if (!this.replaceImageComponent) {
        this.$message.warning('请先选择图片')
        return
      }

      const tagName = this.replaceImageComponent.get('tagName')
      const attrs = this.replaceImageComponent.getAttributes()
      let currentUrl = ''

      if (tagName === 'img') {
        currentUrl = attrs.src || ''
      } else {
        const styles = this.replaceImageComponent.getStyle()
        const bgImage = styles['background-image'] || styles.backgroundImage
        if (bgImage) {
          const match = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/)
          if (match) {
            currentUrl = match[1]
          }
        }
      }

      this.aiImagePrompt = '正在重新生成prompt...'
      this.generateSmartImagePrompt(currentUrl, this.imageContext)
    },
    // AI生成图片
    async handleAIGenerateImage() {
      if (!this.aiImagePrompt.trim()) {
        this.$message.warning('请输入图片描述')
        return
      }

      if (!this.replaceImageComponent) {
        this.$message.error('未选择组件')
        return
      }

      this.aiGeneratingImage = true
      this.aiImageGenerationLog = ''

      try {
        // 获取token和projectId
        const token = this.$store.getters.token
        const projectId = this.$store.getters.projectId

        // 计算压缩宽度
        let compressWidth = null
        if (this.toolbarCompressMode === '2x') {
          // 2倍宽度：尝试从当前图片URL提取宽度
          const component = this.replaceImageComponent
          const tagName = component.get('tagName')
          let currentUrl = ''

          if (tagName === 'img') {
            currentUrl = component.getAttributes().src || ''
          } else {
            const styles = component.getStyle()
            const bgImage = styles['background-image'] || styles.backgroundImage
            if (bgImage) {
              const match = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/)
              if (match) {
                currentUrl = match[1]
              }
            }
          }

          // 尝试从URL提取宽度参数
          try {
            const urlObj = new URL(currentUrl)
            const widthParam = urlObj.searchParams.get('w')
            if (widthParam) {
              compressWidth = parseInt(widthParam, 10) * 2
              console.log(`[工具栏] 2倍宽度: ${widthParam}px → ${compressWidth}px`)
            }
          } catch (e) {
            // URL解析失败，忽略
          }
        } else if (this.toolbarCompressMode === 'custom') {
          compressWidth = this.toolbarCompressWidth
          console.log(`[工具栏] 自定义宽度: ${compressWidth}px`)
        } else {
          console.log('[工具栏] 原图压缩（不调整尺寸）')
        }

        // 构建请求体
        const requestBody = {
          post_id: 0,
          page_title: this.pageInfo.title || '页面配图',
          website_id: this.pageInfo.website_id, // 修复：使用 website_id 而不是 website
          custom_prompt: this.aiImagePrompt,
          style: 'professional',
          aspect_ratio: this.aiImageAspectRatio,
          compress_quality: this.toolbarCompressQuality
        }

        // 添加压缩宽度（如果有）
        if (compressWidth) {
          requestBody.compress_width = compressWidth
        }

        console.log('[工具栏] AI生成请求参数:', requestBody)

        // 使用fetch API处理streaming响应
        const response = await fetch(process.env.VUE_APP_BASE_API + '/cms/api/ai_task_feature_image_generate/', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': token,
            'PTOKEN': projectId
          },
          body: JSON.stringify(requestBody)
        })

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        const reader = response.body.getReader()
        const decoder = new TextDecoder()
        let result = null

        // eslint-disable-next-line no-constant-condition
        while (true) {
          const { value, done } = await reader.read()
          if (done) break

          const chunk = decoder.decode(value, { stream: true })

          // 处理每一行数据
          const lines = chunk.split('\n')
          for (const line of lines) {
            if (line.startsWith('data: ')) {
              const content = line.substring(6)

              // 检查是否包含最终结果
              const match = content.match(/<!--FINAL_RESULT:\s*(.+?)\s*-->/)
              if (match) {
                try {
                  result = JSON.parse(match[1])
                } catch (e) {
                  console.error('解析最终结果失败:', e)
                }
              } else {
                // 实时显示日志
                this.aiImageGenerationLog += content + '\n'
              }
            }
          }
        }

        // 处理最终结果
        if (result && result.success && result.images && result.images.length > 0) {
          // 获取生成的图片信息对象（包含url, local_url, cloud_url等）
          const generatedImage = result.images[0]
          const generatedImageUrl = generatedImage.url

          console.log('[AI生成] 图片信息:', {
            url: generatedImage.url,
            local_url: generatedImage.local_url,
            cloud_url: generatedImage.cloud_url,
            filename: generatedImage.filename
          })

          // 获取旧URL
          const component = this.replaceImageComponent
          const tagName = component.get('tagName')
          let oldUrl = ''

          if (tagName === 'img') {
            oldUrl = component.getAttributes().src || ''
          } else {
            const styles = component.getStyle()
            const oldBgImage = styles['background-image'] || styles.backgroundImage
            if (oldBgImage) {
              const match = oldBgImage.match(/url\(['"]?([^'"]+)['"]?\)/)
              if (match) {
                oldUrl = match[1]
              }
            }
          }

          if (!oldUrl) {
            // 如果没有旧URL，直接设置新URL（新图片场景）
            if (tagName === 'img') {
              component.set('src', generatedImageUrl)
              component.addAttributes({ src: generatedImageUrl })
            } else {
              const currentStyles = component.getStyle()
              currentStyles['background-image'] = `url("${generatedImageUrl}")`
              component.setStyle(currentStyles)
            }
            // 新图片需要手动保存
            this.saveContent()
            this.$message.success('✓ AI生成成功，图片已添加！')
          } else {
            // 使用统一的替换方法（自动保存），传递完整图片信息对象
            console.log('[AI生成] 旧URL:', oldUrl)
            console.log('[AI生成] 新图片信息:', generatedImage)

            const replaced = await this.replaceImage(oldUrl, generatedImage)

            if (replaced) {
              this.$message.success('✓ AI生成成功，图片已替换并保存！')
            } else {
              this.$message.warning('AI生成成功，但替换失败')
            }
          }

          // 延迟1.5秒后自动关闭对话框，让用户能看到替换效果
          setTimeout(() => {
            this.replaceImageDialogVisible = false
            this.aiImagePrompt = ''
            this.aiImageGenerationLog = ''
            this.replaceImageComponent = null
          }, 1500)
        } else {
          this.$message.error(result?.error || 'AI生成图片失败')
        }
      } catch (error) {
        console.error('AI生成图片失败:', error)
        this.$message.error('AI生成图片失败: ' + (error.message || '未知错误'))
        this.aiImageGenerationLog += '\n\n错误: ' + error.message
      } finally {
        this.aiGeneratingImage = false
      }
    },

    // 添加随机图片检测按钮到面板
    addRandomImageDetector(editor) {
      // 添加命令：检测随机图片
      editor.Commands.add('detect-random-images', {
        run: () => {
          this.detectRandomImages()
        }
      })

      // 添加按钮到面板
      const panels = editor.Panels
      panels.addButton('options', {
        id: 'detect-random-images',
        className: 'fa fa-image',
        command: 'detect-random-images',
        attributes: { title: '检测随机图片' }
      })
    },

    // 检测页面中的所有图片（包括随机图片和常规图片）
    detectRandomImages() {
      if (!this.grapesJSEditor) {
        this.$message.warning('编辑器未初始化')
        return
      }

      const wrapper = this.grapesJSEditor.getWrapper()
      const randomImages = []

      // 1. 检测所有 img 标签（不仅仅是random-image）
      const allImgComponents = wrapper.find('img')
      allImgComponents.forEach(comp => {
        const src = comp.get('src') || comp.getAttributes().src || ''

        // 跳过空src和data:开头的base64图片
        if (!src || src.startsWith('data:')) {
          return
        }

        // 判断是否是随机图片
        const isRandomImage = src.includes('random-image') || src.includes('grapesjs.com')

        // 尝试从URL提取宽度参数
        let detectedWidth = null
        try {
          const urlObj = new URL(src)
          const widthParam = urlObj.searchParams.get('w')
          if (widthParam) {
            detectedWidth = parseInt(widthParam, 10)
          }
        } catch (e) {
          // URL解析失败，忽略
        }

        randomImages.push({
          component: comp,
          type: 'img',
          src: src,
          alt: comp.getAttributes().alt || 'IMG图片',
          id: comp.getId(),
          generating: false,
          aspectRatio: '3:2',
          isRandomImage: isRandomImage, // 标记是否是随机图片
          // 压缩配置
          detectedWidth: detectedWidth,
          compressMode: '2x', // 默认2倍宽度
          compressWidth: detectedWidth ? detectedWidth * 2 : 800,
          compressQuality: 100
        })
      })

      // 2. 检测CSS规则中的背景图片（检测所有背景图片，不仅仅是random-image）
      console.log('开始检测CSS规则中的背景图片...')
      const cssRules = this.grapesJSEditor.CssComposer.getAll()

      cssRules.each(rule => {
        const style = rule.getStyle()
        const bgImage = style['background-image'] || ''

        // 检测所有包含url()的背景图片
        if (bgImage && bgImage.includes('url(')) {
          const urlPattern = /url\((?:"([^"]*)"|'([^']*)'|([^)]*))\)/g
          let match
          while ((match = urlPattern.exec(bgImage)) !== null) {
            const bgUrl = match[1] || match[2] || match[3]

            // 跳过空URL、data:开头的base64图片、以及无效URL
            if (!bgUrl || bgUrl.startsWith('data:') || bgUrl === 'none') {
              continue
            }

            console.log('  提取到URL:', bgUrl)

            // 判断是否是随机图片
            const isRandomImage = bgUrl.includes('random-image') || bgUrl.includes('grapesjs.com')

            // 尝试从URL提取宽度参数
            let detectedWidth = null
            try {
              const urlObj = new URL(bgUrl)
              const widthParam = urlObj.searchParams.get('w')
              if (widthParam) {
                detectedWidth = parseInt(widthParam, 10)
              }
            } catch (e) {
              // URL解析失败，忽略
            }

            // 尝试找到使用这个CSS规则的组件
            const selector = rule.selectorsToString()
            const components = wrapper.find(selector)

            if (components.length > 0) {
              // 找到了使用该CSS规则的组件，提取更多上下文
              const comp = components[0]
              let displayName = `CSS背景 (${selector})`

              // 尝试从组件获取更多描述信息
              if (comp.view && comp.view.el) {
                const textContent = comp.view.el.innerText.substring(0, 50)
                if (textContent) {
                  displayName = `CSS背景 (${selector}) - "${textContent}${textContent.length >= 50 ? '...' : ''}"`
                }
              }

              randomImages.push({
                component: comp,
                type: 'background',
                src: bgUrl,
                alt: displayName,
                id: comp.getId(),
                generating: false,
                aspectRatio: '21:9', // CSS背景图片默认使用21:9（横幅/Banner尺寸）
                cssRule: rule, // 保存CSS规则引用，用于后续替换
                isRandomImage: isRandomImage, // 标记是否是随机图片
                // 压缩配置
                detectedWidth: detectedWidth,
                compressMode: '2x',
                compressWidth: detectedWidth ? detectedWidth * 2 : 800,
                compressQuality: 100
              })
            } else {
              // 如果找不到组件，也添加到列表，但component为null
              randomImages.push({
                component: null,
                type: 'background',
                src: bgUrl,
                alt: `CSS背景 (${selector})`,
                id: 'css-' + Math.random().toString(36).substr(2, 9),
                generating: false,
                aspectRatio: '21:9', // CSS背景图片默认使用21:9（横幅/Banner尺寸）
                cssRule: rule, // 保存CSS规则引用
                isRandomImage: isRandomImage,
                // 压缩配置
                detectedWidth: detectedWidth,
                compressMode: '2x',
                compressWidth: detectedWidth ? detectedWidth * 2 : 800,
                compressQuality: 100
              })
            }
          }
        }
      })

      // 3. 检测所有组件的内联背景图片（只检测内联样式，不检测computed style，避免与CSS规则重复）
      const allComponents = wrapper.find('*')
      console.log(`开始检测组件内联样式，共 ${allComponents.length} 个组件`)

      allComponents.forEach(comp => {
        // 只检查内联样式（不检查computed style，因为CSS规则已经在第2步处理了）
        const styles = comp.getStyle()
        const bgImage = styles['background-image'] || styles.backgroundImage || ''

        // 检查背景图片是否包含 url()
        if (bgImage && bgImage.includes('url(')) {
          // 提取URL
          const urlPattern = /url\((?:"([^"]*)"|'([^']*)'|([^)]*))\)/g
          let match
          while ((match = urlPattern.exec(bgImage)) !== null) {
            const bgUrl = match[1] || match[2] || match[3]

            // 跳过空URL、data:开头的base64图片
            if (!bgUrl || bgUrl.startsWith('data:') || bgUrl === 'none') {
              continue
            }

            // 判断是否是随机图片
            const isRandomImage = bgUrl.includes('random-image') || bgUrl.includes('grapesjs.com')

            // 尝试从URL提取宽度参数
            let detectedWidth = null
            try {
              const urlObj = new URL(bgUrl)
              const widthParam = urlObj.searchParams.get('w')
              if (widthParam) {
                detectedWidth = parseInt(widthParam, 10)
              }
            } catch (e) {
              // URL解析失败，忽略
            }

            // 检查是否已经通过CSS规则添加过这个组件
            const alreadyAdded = randomImages.some(img => img.id === comp.getId() && img.src === bgUrl)

            if (!alreadyAdded) {
              randomImages.push({
                component: comp,
                type: 'background',
                src: bgUrl,
                alt: `内联背景 (${comp.get('tagName')})`,
                id: comp.getId(),
                generating: false,
                aspectRatio: '21:9', // CSS背景图片默认使用21:9（横幅/Banner尺寸）
                isRandomImage: isRandomImage,
                // 压缩配置
                detectedWidth: detectedWidth,
                compressMode: '2x',
                compressWidth: detectedWidth ? detectedWidth * 2 : 800,
                compressQuality: 100
              })
            }
          }
        }
      })

      this.randomImages = randomImages

      // 统计随机图片和常规图片数量
      const randomCount = randomImages.filter(i => i.isRandomImage).length
      const normalCount = randomImages.length - randomCount

      if (randomImages.length > 0) {
        this.randomImagesDialogVisible = true
        const imgCount = randomImages.filter(i => i.type === 'img').length
        const bgCount = randomImages.filter(i => i.type === 'background').length
        this.$message.info(
          `检测到 ${randomImages.length} 张图片 ` +
          `(IMG: ${imgCount}, CSS背景: ${bgCount}) | ` +
          `随机图片: ${randomCount}, 常规图片: ${normalCount}`
        )
      } else {
        this.$message.success('未检测到图片')
      }
    },

    // 选中并跳转到指定的随机图片组件
    selectRandomImage(imageItem) {
      if (!this.grapesJSEditor) {
        return
      }

      // 如果没有关联组件（纯CSS规则），提示用户
      if (!imageItem.component) {
        this.$message.warning(`这是一个CSS规则中的背景图片，当前未被任何组件使用`)
        return
      }

      // 选中组件
      this.grapesJSEditor.select(imageItem.component)

      // 滚动到组件位置
      const view = imageItem.component.view
      if (view && view.el) {
        view.el.scrollIntoView({ behavior: 'smooth', block: 'center' })
      }

      this.$message.success(`已定位到图片: ${imageItem.alt || imageItem.id}`)
    },

    // 处理表格勾选变化
    handleSelectionChange(selection) {
      this.selectedRandomImages = selection
    },

    // 选择所有随机图片（排除CSS背景图片）
    selectAllRandomImages() {
      // 过滤条件：是随机图片 且 不是CSS背景类型
      const randomImagesList = this.randomImages.filter(img =>
        img.isRandomImage && img.type !== 'background'
      )
      this.$nextTick(() => {
        randomImagesList.forEach(row => {
          this.$refs.randomImagesTable.toggleRowSelection(row, true)
        })
      })
      this.$message.success(`已选中 ${randomImagesList.length} 张随机图片（已排除CSS背景图片）`)
    },

    // 全选所有图片
    selectAllImages() {
      this.$nextTick(() => {
        this.randomImages.forEach(row => {
          this.$refs.randomImagesTable.toggleRowSelection(row, true)
        })
      })
      this.$message.success(`已全选 ${this.randomImages.length} 张图片`)
    },

    // 清空选择
    clearSelection() {
      this.$refs.randomImagesTable.clearSelection()
      this.$message.info('已清空选择')
    },

    // 为随机图片行设置特殊样式
    tableRowClassName({ row }) {
      if (row.isRandomImage) {
        return 'random-image-row'
      }
      return ''
    },

    // 手动替换图片
    handleManualReplace(imageItem) {
      this.$prompt('请输入新图片的URL地址', '手动替换图片', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputPattern: /^https?:\/\/.+/,
        inputErrorMessage: '请输入有效的图片URL地址（http或https开头）',
        inputPlaceholder: '例如: https://example.com/image.jpg'
      }).then(async({ value: newUrl }) => {
        try {
          console.log(`[手动替换] 开始替换图片 (${imageItem.type})`)
          console.log(`  - 旧URL:`, imageItem.src)
          console.log(`  - 新URL:`, newUrl)

          // 使用统一的替换方法（自动保存）
          const replaced = await this.replaceImage(imageItem.src, newUrl)

          if (!replaced) {
            throw new Error('替换失败，未找到旧URL')
          }

          // 更新列表中的显示
          imageItem.src = newUrl

          this.$message.success('✓ 图片已更新并保存')
        } catch (error) {
          console.error('替换图片失败:', error)
          this.$message.error('替换图片失败: ' + error.message)
        }
      }).catch(() => {
        // 用户取消
      })
    },

    // 单个自动生成并替换
    async autoGenerateAndReplace(imageItem) {
      // 如果是 img 类型，必须有组件
      if (imageItem.type === 'img' && !imageItem.component) {
        this.$message.error('组件不存在')
        return
      }

      // 如果是 background 类型但没有组件，检查是否有 cssRule
      if (imageItem.type === 'background' && !imageItem.component && !imageItem.cssRule) {
        this.$message.error('无法定位到CSS规则')
        return
      }

      // 设置生成状态
      this.$set(imageItem, 'generating', true)

      try {
        // 1. 智能生成prompt
        const promptResult = await this.generateSmartPromptForImage(imageItem)
        if (!promptResult) {
          throw new Error('生成prompt失败')
        }

        console.log(`[${imageItem.alt}] 生成的prompt:`, promptResult)

        // 2. AI生成图片（使用选择的尺寸，background类型默认21:9，img类型默认3:2）
        const aspectRatio = imageItem.aspectRatio || (imageItem.type === 'background' ? '21:9' : '3:2')

        // 从imageItem中读取压缩配置
        let compressWidth = null
        const compressMode = imageItem.compressMode || '2x'
        const detectedWidth = imageItem.detectedWidth

        if (compressMode === 'original') {
          // 原图压缩，不指定宽度
          compressWidth = null
          console.log(`[${imageItem.alt}] 压缩模式: 原图压缩（不调整尺寸）`)
        } else if (compressMode === '2x') {
          // 2倍宽度策略
          if (detectedWidth) {
            compressWidth = detectedWidth * 2
            console.log(`[${imageItem.alt}] 压缩模式: 2倍宽度 (${detectedWidth}px → ${compressWidth}px)`)
          } else {
            console.log(`[${imageItem.alt}] 未检测到原图宽度，使用原图压缩`)
          }
        } else if (compressMode === 'custom') {
          // 自定义宽度
          compressWidth = imageItem.compressWidth || 800
          console.log(`[${imageItem.alt}] 压缩模式: 自定义宽度 (${compressWidth}px)`)
        }

        const generatedImage = await this.generateImageFromPrompt(
          promptResult,
          aspectRatio,
          compressWidth,
          imageItem.compressQuality || 100
        )
        if (!generatedImage) {
          throw new Error('生成图片失败')
        }

        console.log(`[${imageItem.alt}] 生成的图片信息:`, {
          url: generatedImage.url,
          local_url: generatedImage.local_url,
          cloud_url: generatedImage.cloud_url,
          filename: generatedImage.filename
        })

        // 3. 使用统一的替换方法（支持IMG和CSS background，自动保存）
        // 传递完整的图片信息对象
        console.log(`[${imageItem.alt}] 开始替换图片 (${imageItem.type})`)
        const replaced = await this.replaceImage(imageItem.src, generatedImage)

        if (!replaced) {
          throw new Error('替换失败，未找到旧URL')
        }

        // 更新列表中的src显示（使用图片的URL）
        imageItem.src = generatedImage.url

        this.$message.success(`✓ ${imageItem.alt} 已成功替换并保存`)
      } catch (error) {
        console.error(`生成失败 [${imageItem.alt}]:`, error)
        this.$message.error(`${imageItem.alt} 生成失败: ${error.message}`)
      } finally {
        this.$set(imageItem, 'generating', false)
      }
    },

    // 批量自动生成并替换
    async batchAutoGenerateAndReplace() {
      if (this.selectedRandomImages.length === 0) {
        this.$message.warning('请先选择要替换的图片')
        return
      }

      this.batchGenerating = true
      let successCount = 0
      let failCount = 0

      this.$message.info(`开始批量处理 ${this.selectedRandomImages.length} 张图片...`)

      for (const imageItem of this.selectedRandomImages) {
        try {
          await this.autoGenerateAndReplace(imageItem)
          successCount++
        } catch (error) {
          failCount++
          console.error('批量处理失败:', error)
        }
      }

      this.batchGenerating = false
      this.$message.success(`批量处理完成！成功: ${successCount}, 失败: ${failCount}`)

      // 清空选择
      this.$refs.randomImagesTable.clearSelection()
    },

    // 为图片智能生成prompt
    async generateSmartPromptForImage(imageItem) {
      const alt = imageItem.alt || '图片'
      const aspectRatio = imageItem.aspectRatio || (imageItem.type === 'background' ? '21:9' : '3:2')

      // 为CSS背景图片提取更丰富的上下文信息
      let contextInfo = alt
      let elementInfo = ''

      if (imageItem.type === 'background') {
        // 提取选择器名称
        const selectorMatch = alt.match(/CSS背景 \((.+?)\)/)
        const selector = selectorMatch ? selectorMatch[1] : ''

        // 尝试获取元素的文本内容和其他信息
        if (imageItem.component) {
          const comp = imageItem.component
          const tagName = comp.get('tagName') || 'div'
          const textContent = comp.view && comp.view.el ? comp.view.el.innerText.substring(0, 200) : ''
          const classes = comp.getClasses().join(' ')

          elementInfo = `元素类型: ${tagName}\n`
          if (classes) elementInfo += `CSS类: ${classes}\n`
          if (textContent) elementInfo += `文本内容: ${textContent}\n`
        } else if (selector) {
          // 如果没有组件但有选择器，尝试从选择器推断用途
          elementInfo = `CSS选择器: ${selector}\n`
          if (selector.includes('hero')) {
            elementInfo += '用途: 网站首屏大图/英雄区域背景\n'
          } else if (selector.includes('banner')) {
            elementInfo += '用途: 横幅/广告位背景\n'
          } else if (selector.includes('section')) {
            elementInfo += '用途: 页面区块背景\n'
          } else if (selector.includes('header')) {
            elementInfo += '用途: 页眉背景\n'
          } else if (selector.includes('footer')) {
            elementInfo += '用途: 页脚背景\n'
          } else if (selector.includes('card')) {
            elementInfo += '用途: 卡片背景\n'
          }
        }

        // 组合上下文信息
        contextInfo = `${alt}\n${elementInfo}`.trim()
      }

      // 调用智能prompt生成接口
      try {
        const response = await request({
          url: '/static_web/api/ai/generate_image_prompt/',
          method: 'post',
          data: {
            page_id: this.pageId,
            image_context: contextInfo, // 使用增强后的上下文
            image_src: imageItem.src,
            aspect_ratio: aspectRatio, // 使用选择的尺寸
            image_type: imageItem.type, // 传递图片类型 (img/background)
            element_info: elementInfo // 单独传递元素信息
          }
        })

        if (response.success && response.data && response.data.prompt) {
          return response.data.prompt
        } else {
          throw new Error('生成prompt失败')
        }
      } catch (error) {
        console.error('调用智能prompt生成失败:', error)
        // 降级方案：使用增强后的上下文作为简单prompt
        if (imageItem.type === 'background') {
          return `Professional high-quality background image for ${contextInfo}`
        } else {
          return `Professional high-quality image for: ${alt}`
        }
      }
    },

    // 从prompt生成图片
    async generateImageFromPrompt(prompt, aspectRatio = '3:2', compressWidth = null, quality = 100) {
      const token = this.$store.getters.token
      const projectId = this.$store.getters.projectId

      try {
        // 构建请求体
        const requestBody = {
          post_id: 0,
          page_title: this.pageInfo.title || '页面配图',
          website_id: this.pageInfo.website_id, // 🔧 修复: 传递网站ID用于OSS路径和Cloudflare上传
          custom_prompt: prompt,
          style: 'professional',
          aspect_ratio: aspectRatio // 使用传入的尺寸参数
        }

        // 添加压缩参数
        if (compressWidth) {
          requestBody.compress_width = compressWidth // 使用新的参数名
        }
        if (quality) {
          requestBody.compress_quality = quality
        }

        const response = await fetch(process.env.VUE_APP_BASE_API + '/cms/api/ai_task_feature_image_generate/', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': token,
            'PTOKEN': projectId
          },
          body: JSON.stringify(requestBody)
        })

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        const reader = response.body.getReader()
        const decoder = new TextDecoder()
        let result = null

        // eslint-disable-next-line no-constant-condition
        while (true) {
          const { value, done } = await reader.read()
          if (done) break

          const chunk = decoder.decode(value, { stream: true })
          const lines = chunk.split('\n')

          for (const line of lines) {
            if (line.startsWith('data: ')) {
              const content = line.substring(6)
              const match = content.match(/<!--FINAL_RESULT:\s*(.+?)\s*-->/)
              if (match) {
                try {
                  result = JSON.parse(match[1])
                } catch (e) {
                  console.error('解析最终结果失败:', e)
                }
              }
            }
          }
        }

        if (result && result.success && result.images && result.images.length > 0) {
          // 返回完整的图片信息对象，而不仅仅是URL
          return result.images[0]
        } else {
          throw new Error(result?.error || '生成图片失败')
        }
      } catch (error) {
        console.error('生成图片失败:', error)
        throw error
      }
    },

    // 统一的图片URL替换函数（适用于IMG src和CSS background）
    // 原理：组合完整HTML → 字符串替换 → 保存到服务器 → 重新加载编辑器
    //
    // @param {string} oldUrl - 旧的图片URL
    // @param {string|Object} newUrlOrImageInfo - 新的图片URL字符串 或 完整的图片信息对象
    //   如果是对象，应包含: { url, local_url, cloud_url, filename }
    async replaceImage(oldUrl, newUrlOrImageInfo) {
      // AI图片替换：直接在完整HTML字符串上操作，与编辑器无关

      // 兼容处理：支持传入字符串URL或对象
      let newUrl = ''
      let localUrl = null

      if (typeof newUrlOrImageInfo === 'string') {
        // 传入的是字符串URL（向后兼容）
        newUrl = newUrlOrImageInfo
      } else if (typeof newUrlOrImageInfo === 'object' && newUrlOrImageInfo.url) {
        // 传入的是完整图片信息对象
        newUrl = newUrlOrImageInfo.url
        localUrl = newUrlOrImageInfo.local_url || null
      } else {
        console.error('❌ newUrlOrImageInfo 参数格式错误')
        return false
      }

      console.log('🔄 AI图片替换（字符串替换，不涉及编辑器）')
      console.log('  - 旧URL:', oldUrl)
      console.log('  - 新URL:', newUrl)
      if (localUrl) {
        console.log('  - 本地路径:', localUrl)
      }

      // 步骤1: 直接在完整HTML字符串上替换图片URL
      let fullHtml = this.currentHtmlContent
      const beforeReplace = fullHtml
      fullHtml = fullHtml.replaceAll(oldUrl, newUrl)
      const replaced = beforeReplace !== fullHtml

      if (!replaced) {
        console.warn('⚠️ 未找到旧URL，替换失败')
        return false
      }

      console.log('✓ HTML字符串替换完成')

      // 步骤2: 如果有本地路径，更新所有<img>标签的data-final-src属性
      if (localUrl) {
        // 转换本地路径为相对路径：
        // /static_sites/{domain}/images/{md5}/{filename} → assets/images/{md5}/{filename}
        const relativeLocalUrl = this.convertToRelativePath(localUrl)

        if (relativeLocalUrl) {
          console.log('  - 相对路径:', relativeLocalUrl)

          // 正则匹配所有包含新URL的<img>标签
          const imgRegex = /<img([^>]*?)src=["']([^"']*?)["']([^>]*?)>/gi
          fullHtml = fullHtml.replace(imgRegex, (match, before, src, after) => {
            // 只处理src匹配新URL的img标签
            if (src === newUrl) {
              // 检查是否已经有data-final-src属性
              if (after.includes('data-final-src=')) {
                // 替换现有的data-final-src值
                const updated = after.replace(/data-final-src=["']([^"']*?)["']/i, `data-final-src="${relativeLocalUrl}"`)
                return `<img${before}src="${src}"${updated}>`
              } else {
                // 添加新的data-final-src属性
                return `<img${before}src="${src}" data-final-src="${relativeLocalUrl}"${after}>`
              }
            }
            return match
          })

          console.log('✓ data-final-src 属性已更新')
        }
      }

      // 步骤3: 更新内存中的完整HTML
      this.currentHtmlContent = fullHtml

      // 步骤4: 保存到服务器
      try {
        await request({
          url: `/static_web/api/page/${this.pageId}/save_html/`,
          method: 'post',
          data: {
            html_content: this.currentHtmlContent
          }
        })
        console.log('✓ 已保存到服务器')
      } catch (error) {
        console.error('保存到服务器失败:', error)
        throw error
      }

      // 步骤5: 重新加载到编辑器（如果编辑器已初始化）
      if (this.grapesJSEditor) {
        // 重新提取HTML各部分
        this.extractHtmlParts()

        // 重新加载body内容到编辑器
        const bodyContentOnly = this.extractBodyContent(fullHtml)
        this.grapesJSEditor.setComponents(bodyContentOnly)

        // 重新加载CSS到编辑器
        const styleMatch = fullHtml.match(/<style[^>]*>([\s\S]*?)<\/style>/i)
        if (styleMatch) {
          this.grapesJSEditor.setStyle(styleMatch[1])
        }

        console.log('✓ 编辑器已重新加载')
      }

      return true
    },

    // 将本地绝对路径转换为相对路径
    // /static_sites/{domain}/images/{md5}/{filename} → assets/images/{md5}/{filename}
    convertToRelativePath(localUrl) {
      if (!localUrl) return null

      // 匹配格式: /static_sites/{domain}/images/{md5}/{filename}
      const match = localUrl.match(/\/static_sites\/[^/]+\/images\/(.+)/)
      if (match && match[1]) {
        return `assets/images/${match[1]}`
      }

      // 如果是通用目录格式: /static_sites/images/{md5}/{filename}
      const generalMatch = localUrl.match(/\/static_sites\/images\/(.+)/)
      if (generalMatch && generalMatch[1]) {
        return `assets/images/${generalMatch[1]}`
      }

      return null
    },

    // ⚠️ 废弃：旧的背景图片替换方法（保留用于向后兼容）
    replaceAllBackgroundImages(oldUrl, newUrl) {
      console.warn('replaceAllBackgroundImages已废弃，使用replaceImage代替')
      return this.replaceImage(oldUrl, newUrl) ? 1 : 0
    },

    // 自动保存页面（静默保存，不显示成功提示）
    // 仅用于保存 CSS 修改，不重新获取 HTML，避免丢失 script
    async autoSavePage() {
      if (!this.grapesJSEditor) {
        return
      }

      try {
        console.log('💾 autoSavePage - 仅更新 GrapeJS CSS，不触动 HTML 结构')

        // 只获取最新的 CSS（不获取 HTML，避免丢失 script）
        const css = this.grapesJSEditor.getCss()

        // 在当前的 currentHtmlContent 中更新 GrapeJS 样式标签
        let updatedHtml = this.currentHtmlContent

        // 更新或插入 <style data-gjs-type="wrapper">（GrapeJS生成的样式）
        const grapesJsStyleTag = `<style data-gjs-type="wrapper">\n${css}\n  </style>`

        if (/<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i.test(updatedHtml)) {
          // 替换现有的GrapeJS style标签
          updatedHtml = updatedHtml.replace(
            /<style[^>]*data-gjs-type=["']wrapper["'][^>]*>[\s\S]*?<\/style>/i,
            grapesJsStyleTag
          )
          console.log('✓ 已更新GrapeJS样式标签')
        } else {
          // 插入新的GrapeJS style标签（插入到</head>之前）
          updatedHtml = updatedHtml.replace(
            /(<\/head>)/i,
            `  ${grapesJsStyleTag}\n$1`
          )
          console.log('✓ 已插入GrapeJS样式标签')
        }

        this.currentHtmlContent = updatedHtml

        // 验证 script 是否保留
        const scriptCount = (updatedHtml.match(/<script/gi) || []).length
        console.log(`💾 保存后 HTML 包含 ${scriptCount} 个 script 标签`)

        if (scriptCount === 0) {
          console.warn('⚠️ 警告：HTML 中没有 script 标签（可能页面本身就没有）')
        }

        // 保存到服务器
        await request({
          url: `/static_web/api/page/${this.pageId}/save_html/`,
          method: 'post',
          data: {
            html_content: this.currentHtmlContent
          }
        })

        console.log('✓ 页面已自动保存（仅更新 GrapeJS CSS）')

        // 重新提取 HTML 部分（确保 originalHtmlParts 是最新的）
        this.extractHtmlParts()

        // 重新加载到 GrapeJS 编辑器，让用户看到最新效果
        const bodyContentOnly = this.extractBodyContent(this.currentHtmlContent)
        this.grapesJSEditor.setComponents(bodyContentOnly)

        // 重新加载 CSS
        const styleMatch = this.currentHtmlContent.match(/<style[^>]*data-gjs-type=["']wrapper["'][^>]*>([\s\S]*?)<\/style>/i)
        if (styleMatch) {
          this.grapesJSEditor.setStyle(styleMatch[1])
        }

        console.log('✓ 编辑器已重新渲染')
      } catch (error) {
        console.error('自动保存失败:', error)
        // 自动保存失败不显示错误提示，避免打扰用户
      }
    }
  }
}
</script>

<style scoped>
.grapesjs-editor-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: #f5f7fa;
  display: flex;
  flex-direction: column;
  z-index: 100;
}

/* 顶部工具栏 */
.top-toolbar {
  height: 60px;
  background: white;
  border-bottom: 1px solid #dcdfe6;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.toolbar-left,
.toolbar-right {
  display: flex;
  align-items: center;
  gap: 15px;
}

.page-title {
  font-size: 16px;
  font-weight: 500;
  color: #303133;
  margin-left: 10px;
}

/* GrapeJS 编辑器 */
.grapes-editor {
  flex: 1;
  width: 100%;
  overflow: hidden;
}

/* 随机图片行高亮样式 */
.el-table >>> .random-image-row {
  background-color: #fff3e0 !important;
}

.el-table >>> .random-image-row:hover > td {
  background-color: #ffe0b2 !important;
}
</style>

<style>
/* GrapeJS 全局样式 */
#grapesjs-editor {
  position: relative;
  height: 100%;
}

#grapesjs-editor .gjs-cv-canvas {
  background-color: #f5f7fa;
}

#grapesjs-editor .gjs-frame-wrapper {
  border: none !important;
}

/* 工具栏按钮样式优化 */
.gjs-toolbar {
  display: flex !important;
  flex-direction: row !important;
  gap: 2px !important;
  padding: 2px !important;
  background: rgba(0, 0, 0, 0.8) !important;
  border-radius: 4px !important;
}

.gjs-toolbar-item {
  min-width: 28px !important;
  min-height: 28px !important;
  width: auto !important;
  height: auto !important;
  padding: 4px 6px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  background: transparent !important;
  border: none !important;
  color: white !important;
  cursor: pointer !important;
  transition: all 0.2s !important;
  border-radius: 3px !important;
  font-size: 14px !important;
  flex-shrink: 0 !important;
}

.gjs-toolbar-item:hover {
  background: rgba(255, 255, 255, 0.2) !important;
  transform: scale(1.05) !important;
}

/* AI 按钮特殊样式 */
.gjs-toolbar-item .fa-magic,
.gjs-toolbar-item .fa-plus-circle {
  color: #1EB7E6 !important;
  font-size: 14px !important;
}

.gjs-toolbar-item:hover .fa-magic,
.gjs-toolbar-item:hover .fa-plus-circle {
  color: #40d9ff !important;
}

/* 替换图片按钮特殊样式 */
.gjs-toolbar-item .fa-image {
  color: #67C23A !important;
  font-size: 14px !important;
}

.gjs-toolbar-item:hover .fa-image {
  color: #85ce61 !important;
}
</style>
